﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Internal;

namespace System.ComponentModel.Composition
{
    /// <summary>
    ///     Provides extension methods for the <see cref="ICompositionService"/> interface.
    /// </summary>
    public static partial class CompositionServiceExtensions
    {
        public static ComposablePart AddPart(this ICompositionService compositionService, object attributedPart)
        {
            Requires.NotNull(compositionService, "compositionService");
            Requires.NotNull(attributedPart, "attributedPart");

            // TODO: This actually needs to do exactly what the 
            // CompositionContainer itself does, see Dev10 467947.
            ComposablePart part = CompositionServices.CreateAttributedPart(attributedPart);

            compositionService.AddPart(part);

            return part;
        }

        /// <summary>
        ///     Adds the specified object to the composition service, exporting it under a contract 
        ///     name derived from the specified type parameter.
        /// </summary>
        /// <typeparam name="T">
        ///     The type of the object to add. The contract name is also derived from this type 
        ///     parameter.
        /// </typeparam>
        /// <param name="compositionService">
        ///     The <see cref="ICompositionService"/> to add the <see cref="object"/> to.
        /// </param>
        /// <param name="exportedObject">
        ///     The <see cref="object"/> to add to the <see cref="ICompositionService"/>. This can
        ///     be <see langword="null"/>.
        /// </param>
        /// <returns>
        ///     A <see cref="ComposablePart"/> that can be used remove the <see cref="object"/>
        ///     from the <see cref="ICompositionService"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="compositionService"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        ///     The <see cref="ICompositionService"/> has been disposed of.
        /// </exception>
        /// <remarks>
        ///     <para>
        ///         The contract name is the result of calling 
        ///         <see cref="CompositionServices.GetContractName(Type)"/> on <typeparamref name="T"/>.
        ///     </para>
        ///     <para>
        ///         The <see cref="object"/> is not available until 
        ///         <see cref="ICompositionService.Compose"/> is called.
        ///     </para>
        /// </remarks>
        public static ComposablePart AddExportedObject<T>(this ICompositionService compositionService, T exportedObject)
        {
            string contractName = CompositionServices.GetContractName(typeof(T));

            return AddExportedObject(compositionService, contractName, exportedObject);
        }

        /// <summary>
        ///     Adds the specified object to the composition service, exporting it under the specified 
        ///     contract name.
        /// </summary>
        /// <param name="compositionService">
        ///     The <see cref="ICompositionService"/> to add the <see cref="object"/> to.
        /// </param>
        /// <param name="contractName">
        ///     A <see cref="String"/> containing the contract name to export the <see cref="object"/> 
        ///     under.
        /// </param>
        /// <param name="exportedObject">
        ///     The <see cref="object"/> to add to the <see cref="ICompositionService"/>. This can
        ///     be <see langword="null"/>.
        /// </param>
        /// <returns>
        ///     A <see cref="ComposablePart"/> that can be used remove the <see cref="Object"/>
        ///     from the <see cref="ICompositionService"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="compositionService"/> is <see langword="null"/>.
        ///     <para>
        ///         -or-
        ///     </para>
        ///     <paramref name="contractName"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///     <paramref name="contractName"/> is an empty string ("").
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        ///     The <see cref="ICompositionService"/> has been disposed of.
        /// </exception>
        /// <remarks>
        ///     <para>
        ///         The <see cref="object"/> is not available until 
        ///         <see cref="ICompositionService.Compose"/> is called.
        ///     </para>
        /// </remarks>
        [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "object")]
        public static ComposablePart AddExportedObject(this ICompositionService compositionService, string contractName, object exportedObject)
        {
            return AddExport(compositionService, new Export(contractName, () => exportedObject));
        }

        /// <summary>
        ///     Adds the specified export to the composition service.
        /// </summary>
        /// <param name="compositionService">
        ///     The <see cref="ICompositionService"/> to add the <see cref="Export"/> to.
        /// </param>
        /// <param name="export">
        ///     The <see cref="Export"/> to add to the <see cref="ICompositionService"/>.
        /// </param>
        /// <returns>
        ///     A <see cref="ComposablePart"/> that can be used remove the <see cref="Export"/>
        ///     from the <see cref="ICompositionService"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="compositionService"/> is <see langword="null"/>.
        ///     <para>
        ///         -or-
        ///     </para>
        ///     <paramref name="export"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        ///     The <see cref="ICompositionService"/> has been disposed of.
        /// </exception>
        /// <remarks>
        ///     <para>
        ///         The <see cref="Export"/> is not available until 
        ///         <see cref="ICompositionService.Compose"/> is called.
        ///     </para>
        /// </remarks>
        public static ComposablePart AddExport(this ICompositionService compositionService, Export export)
        {
            Requires.NotNull(compositionService, "compositionService");
            Requires.NotNull(export, "export");

            var part = new SingleExportComposablePart(export);
            
            compositionService.AddPart(part);

            return part;
        }
    }
}